﻿namespace Cellfish.Toolbox.Facts.Tasks
{
    using System;
    using System.Collections.Generic;
    using System.Threading;
    using System.Threading.Tasks;

    using Cellfish.Test;
    using Cellfish.Toolbox.Tasks;

    using Microsoft.VisualStudio.TestTools.UnitTesting;

    [TestClass]
    public class TaskWhen_AllOrErrorFacts
    {
        [TestMethod]
        public void Given_null_enumerable_of_tasks_When_waiting_for_all_or_error_Then_ArgumentNullException_is_thrown()
        {
            IEnumerable<Task> tasks1 = null;
            IEnumerable<Task<int>> tasks2 = null;
            Ensure.Throws<ArgumentNullException>(() => TaskWhen.AllOrError(tasks1), "Failed for Task.");
            Ensure.Throws<ArgumentNullException>(() => TaskWhen.AllOrError(tasks2), "Failed for Task<T>.");
        }

        [TestMethod]
        public void Given_no_tasks_When_waiting_for_all_or_error_Then_task_completes()
        {
            Assert.IsTrue(TaskWhen.AllOrError().Wait(TimeSpan.FromSeconds(5)), "Failed for Task.");
            Assert.AreEqual(0, TaskWhen.AllOrError<int>().Result.Length, "Failed for Task<T>.");
        }

        [TestMethod]
        public void Given_two_tasks_When_waiting_for_all_or_error_Then_task_completes()
        {
            var task1 = Task.Delay(TimeSpan.Zero);
            var task2 = Task.Delay(TimeSpan.Zero);
            Assert.IsTrue(TaskWhen.AllOrError(task1, task2).Wait(TimeSpan.FromSeconds(5)), "Failed for Task.");
            Assert.IsTrue(TaskWhen.AllOrError(task1.WithResult(true), task2.WithResult(false)).Wait(TimeSpan.FromSeconds(5)), "Failed for Task<T>.");
        }

        [TestMethod]
        public void Given_two_tasks_where_one_is_failing_When_waiting_for_all_or_error_Then_task_fails()
        {
            var task1 = Task.Delay(TimeSpan.FromSeconds(15));
            var task2 = Task.Factory.StartNew(() => { throw new Exception(); });
            Ensure.TaskFails<Exception>(TaskWhen.AllOrError(task1, task2), "Failed for Task.");
            Ensure.TaskFails<Exception>(TaskWhen.AllOrError(task1.WithResult(1), task2.WithResult(2)), "Failed for Task.");
            Assert.IsFalse(task1.IsCompleted, "Successful task should not be completed.");
        }

        [TestMethod]
        public void Given_two_tasks_where_one_is_cancelled_When_waiting_for_all_or_error_Then_task_is_cancelled()
        {
            var cancellationToken = new CancellationTokenSource();
            var task1 = Task.Delay(TimeSpan.FromSeconds(15), cancellationToken.Token);
            var task2 = Task.Delay(TimeSpan.FromSeconds(15));
            cancellationToken.Cancel();
            Ensure.TaskFails<TaskCanceledException>(TaskWhen.AllOrError(task1, task2), "Failed for Task.");
            Ensure.TaskFails<TaskCanceledException>(TaskWhen.AllOrError(task1.WithResult(1), task2.WithResult(2)), "Failed for Task.");
            Assert.IsFalse(task2.IsCompleted, "Successful task should not be completed.");
        }
    }
}
